Migrating facets
Ucommerce 9.4 introduces changes to price facets and automatic range facets. Below you will find the exact steps you need to take to upgrade your code for version 9.4. If this is the first time reading the article, please take a moment and also read the overview to get a deeper understanding and to find out if any of the indexing improvements could be useful to you.
Concrete steps:
-
In all of your custom index definitions, replace calls to PricesField with
.Facet(p => p.Field["<priceGroupName>"],
new FacetOptions { AutomaticRanges = true } )
for each of the price groups you need faceted. -
In all your queries, remove uses of
PriceGroup()
inIndex.Find().Where(facets).PriceGroup().ToFacets()
. -
Index everything from scratch
ProductsIndexDefinition
Before:
this.PricesField(p => p.UnitPrices); // <- price concept leaking into Bolt
public void MyCustomQuery() { var facets = new FacetDictionary { {"UnitPrices", new[] {"(0 - 100)"}} }; Products.Find() .Where(facets) .PriceGroup("EUR 15 % VAT") // <-- pricegroup concept leaking into Bolt .ToList() }
After:
this.Field(p => p.UnitPrices); this.Field(p => p.PricesInclTax); this.Field(p => p.Taxes);
public void MyCustomQuery() { var productIndex = Ucommerce.Infrastructure.ObjectFactory.Instance.Resolve<IIndex<Product>>(); productIndex.Find() .Where(p => p.UnitPrices["EUR 15 % VAT"] == Match.Range(0, 100)) .ToList(); }
Overview:
In Ucommerce 9.3, unit prices used get automated range calculation. In 9.4, any number of dictionary type fields can have automatic facet ranges.
To allow for this change, we have removed PricesField()
and PriceGroup()
method calls.
By default, the product index contains unit prices, prices incl. tax and taxes, all of type Dictionary<string, decimal>.
However, they are not facets by default, they can now be used to build facet ranges in the same product index.
There is also support for drilling down as well as drilling sideways. That means that facets can be both inclusive(OR) and exclusive(AND).
To enable faceted search, you use the same .Facet()
extension method as with previous versions of Ucommerce 9.
With simple, numerical fields, you just use the field itself:
this.Field(p => p.UnitPrices).Facet().AutoRanges();
With prices grouped by price group (or any other Dictionary<string, TNumeric>
), going forward, you will need to specify one facet per key. Often pricegroup will be the key, so in effect one facet per pricegroup.
this.Field(p => p.UnitPrices); this.Field(p => p.UnitPrices["EUR 15 % VAT"]) .Facet() .AutoRanges(count: 5, precision: 10); this.Field(p => p.UnitPrices["USD 7 % VAT"]) .Facet() .AutoRanges(count: 5, precision: 100);
public void MyCustomQuery2() { var productIndex = Ucommerce.Infrastructure.ObjectFactory.Instance.Resolve<IIndex<Product>>(); var facets = new FacetDictionary { {"UnitPrices.EUR 15 % VAT", new[] {"(0 - 100)"}} }; productIndex.Find() .Where(facets) .ToFacets(); }
Notice also how the facet name itself now is a combination of the field name and the key, separated by a dot. Because the field names have changed within the index, a full “Index from scratch” is needed after an upgrade.
Automatic range precision
Optionally specify the number of ranges and their precision.
If the max value in the result set is 75, the number of ranges are 4 and precision 10, the ranges created will be:
0 - 20, 20 - 40, 40 - 60, 60 - 80.
If the max value in the result set is 211, the number of ranges are 3 and precision 100, the ranges created will be:
0 - 100, 100 - 200, 200 - 300.
Default values are count = 5 and precision = 100
Automatic ranges for Faceted Search
Be aware, using automatic ranges comes with a performance overhead.
To determine the ranges properly, the system needs to reach out to the Index and retrieve max value for the field.
This comes with 5ms - 20ms penalty depending on the setup and latency.
The future releases should include method ToFacetsAsync()
which will parallelize the execution flow reducing the overhead.
Faceting all price groups
If case you have a lot of pricegroups, this is one way to enabled automatic range facets for them all.
this.Field(p => p.UnitPrices); var priceGroupRepository = Ucommerce.Infrastructure.ObjectFactory.Instance.Resolve<Ucommerce.EntitiesV2.IRepository<Ucommerce.EntitiesV2.PriceGroup>>(); var allPriceGroups = priceGroupRepository.Select(p => p.Deleted == false); foreach(var priceGroup in allPriceGroups) { this.Field(p => p.UnitPrices[priceGroup.Name]).Facet().AutoRanges(); }
Elastic induced changes:
In addition to the indexing changes, we are also introducing elastic search. With it, there come a few additional changes. To find out of elastic is something you need, please read this elastic specific article.
The type of facetDictionary from Dictionary<string,IEnumerable<string>>
to Dictionary<string,IEnumerable<object>>
.
Date objects can now be used in the search. Please note that you need to cast DateTime to object.
var date = DateTime.Parse("2100-08-01 10:00:00"); var facetDict = new FacetDictionary { { "CreatedOn", new [] {(object) date } } }; var productIndex = Ucommerce.Infrastructure.ObjectFactory.Instance.Resolve<IIndex<Product>>(); var result = productIndex.Find().Where(facetDict).ToFacets();